home *** CD-ROM | disk | FTP | other *** search
- /* ----------------------------------------------------------------
- * FILE
- * indexscan.c
- *
- * DESCRIPTION
- * Routines to support indexes and indexed scans of relations
- *
- * INTERFACE ROUTINES
- * ExecInsertIndexTuples inserts tuples into indices on result relation
- *
- * ExecIndexScan scans a relation using indices
- * ExecIndexNext using index to retrieve next tuple
- * ExecInitIndexScan creates and initializes state info.
- * ExecIndexReScan rescans the indexed relation.
- * ExecEndIndexScan releases all storage.
- * ExecIndexMarkPos marks scan position.
- * ExecIndexRestrPos restores scan position.
- *
- * NOTES
- * the code supporting ExecInsertIndexTuples should be
- * collected and merged with the genam stuff.
- *
- * IDENTIFICATION
- * $Header: /private/postgres/src/executor/RCS/n_indexscan.c,v 1.15 1992/08/26 19:41:12 mer Exp $
- * ----------------------------------------------------------------
- */
-
- #include "tcop/slaves.h"
- #include "executor/executor.h"
-
- RcsId("$Header: /private/postgres/src/executor/RCS/n_indexscan.c,v 1.15 1992/08/26 19:41:12 mer Exp $");
-
- /* ----------------
- * Misc stuff to move to executor.h soon -cim 6/5/90
- * ----------------
- */
- #define NO_OP 0
- #define LEFT_OP 1
- #define RIGHT_OP 2
-
- /* ----------------------------------------------------------------
- * ExecInsertIndexTuples support
- * ----------------------------------------------------------------
- */
- /* ----------------------------------------------------------------
- * ExecGetIndexKeyInfo
- *
- * Extracts the index key attribute numbers from
- * an index tuple form (i.e. a tuple from the pg_index relation)
- * into an array of attribute numbers. The array and the
- * size of the array are returned to the caller via return
- * parameters.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecOpenIndices
- ****/
- void
- ExecGetIndexKeyInfo(indexTuple, numAttsOutP, attsOutP, fInfoP)
- IndexTupleForm indexTuple;
- int *numAttsOutP;
- AttributeNumberPtr *attsOutP;
- FuncIndexInfoPtr fInfoP;
- {
- int i;
- int numKeys;
- AttributeNumberPtr attKeys;
-
- /* ----------------
- * check parameters
- * ----------------
- */
- if (numAttsOutP == NULL && attsOutP == NULL) {
- elog(DEBUG, "ExecGetIndexKeyInfo: %s",
- "invalid parameters: numAttsOutP and attsOutP must be non-NULL");
- }
-
- /* ----------------
- * set the procid for a possible functional index.
- * ----------------
- */
- FIsetProcOid(fInfoP, indexTuple->indproc);
-
- /* ----------------
- * count the number of keys..
- * ----------------
- */
- numKeys = 0;
- for (i=0; i<8 && indexTuple->indkey[i] != 0; i++)
- numKeys++;
-
- /* ----------------
- * place number keys in callers return area
- * or the number of arguments for a functional index.
- *
- * If we have a functional index then the number of
- * attributes defined in the index must 1 (the function's
- * single return value).
- * ----------------
- */
- if (FIgetProcOid(fInfoP) != InvalidObjectId) {
- FIsetnArgs(fInfoP, numKeys);
- (*numAttsOutP) = 1;
- }
- else
- (*numAttsOutP) = numKeys;
-
- if (numKeys < 1) {
- elog(DEBUG, "ExecGetIndexKeyInfo: %s",
- "all index key attribute numbers are zero!");
- (*attsOutP) = NULL;
- return;
- }
-
- /* ----------------
- * allocate and fill in array of key attribute numbers
- * ----------------
- */
- CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext);
-
- attKeys = (AttributeNumberPtr)
- palloc(numKeys * sizeof(AttributeNumber));
-
- for (i=0; i<numKeys; i++)
- attKeys[i] = indexTuple->indkey[i];
-
- /* ----------------
- * return array to caller.
- * ----------------
- */
- (*attsOutP) = attKeys;
- }
-
- /* ----------------------------------------------------------------
- * ExecOpenIndices
- *
- * Here we scan the pg_index relation to find indices
- * associated with a given heap relation oid. Since we
- * don't know in advance how many indices we have, we
- * form lists containing the information we need from
- * pg_index and then process these lists.
- *
- * Note: much of this code duplicates effort done by
- * the IndexCatalogInformation function in plancat.c
- * because IndexCatalogInformation is poorly written.
- *
- * It would be much better the functionality provided
- * by this function and IndexCatalogInformation was
- * in the form of a small set of orthogonal routines..
- * If you are trying to understand this, I suggest you
- * look at the code to IndexCatalogInformation and
- * FormIndexTuple.. -cim 9/27/89
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * InitPlan
- ****/
- void
- ExecOpenIndices(resultRelationOid, resultRelationInfo)
- ObjectId resultRelationOid;
- RelationInfo resultRelationInfo;
- {
- Relation indexRd;
- HeapScanDesc indexSd;
- ScanKeyData key;
- HeapTuple tuple;
- IndexTupleForm indexStruct;
- ObjectId indexOid;
- List oidList;
- List nkeyList;
- List keyList;
- List fiList;
- List indexoid;
- List numkeys;
- List indexkeys;
- List indexfuncs;
- int len;
-
- RelationPtr relationDescs;
- IndexInfoPtr indexInfoArray;
- FuncIndexInfoPtr fInfoP;
- int numKeyAtts;
- AttributeNumberPtr indexKeyAtts;
- int i;
-
- /* ----------------
- * open pg_index
- * ----------------
- */
- indexRd = amopenr(IndexRelationName);
-
- /* ----------------
- * form a scan key
- * ----------------
- */
- ScanKeyEntryInitialize(&key.data[0], 0, IndexHeapRelationIdAttributeNumber,
- ObjectIdEqualRegProcedure,
- ObjectIdGetDatum(resultRelationOid));
-
- /* ----------------
- * scan the index relation, looking for indices for our
- * result relation..
- * ----------------
- */
- indexSd = ambeginscan(indexRd, /* scan desc */
- false, /* scan backward flag */
- NowTimeQual, /* time qual */
- 1, /* number scan keys */
- &key); /* scan keys */
-
- oidList = LispNil;
- nkeyList = LispNil;
- keyList = LispNil;
- fiList = LispNil;
-
- while(tuple = amgetnext(indexSd, /* scan desc */
- false, /* scan backward flag */
- NULL), /* return: buffer */
- HeapTupleIsValid(tuple)) {
-
- /* ----------------
- * For each index relation we find, extract the information
- * we need and store it in a list..
- *
- * first get the oid of the index relation from the tuple
- * ----------------
- */
- indexStruct = (IndexTupleForm) GETSTRUCT(tuple);
- indexOid = indexStruct->indexrelid;
-
- /* ----------------
- * allocate space for functional index information.
- * ----------------
- */
- fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) );
-
- /* ----------------
- * next get the index key information from the tuple
- * ----------------
- */
- ExecGetIndexKeyInfo(indexStruct,
- &numKeyAtts,
- &indexKeyAtts,
- fInfoP);
-
- /* ----------------
- * save the index information into lists
- * ----------------
- */
- oidList = lispCons(lispInteger(indexOid), oidList);
- nkeyList = lispCons(lispInteger(numKeyAtts), nkeyList);
- keyList = lispCons((LispValue)indexKeyAtts, keyList);
- fiList = lispCons((LispValue)fInfoP, fiList);
- }
-
- /* ----------------
- * we have the info we need so close the pg_index relation..
- * ----------------
- */
- amendscan(indexSd);
- amclose(indexRd);
-
- /* ----------------
- * Now that we've collected the index information into three
- * lists, we open the index relations and store the descriptors
- * and the key information into arrays.
- * ----------------
- */
- len = length(oidList);
- if (len > 0) {
- /* ----------------
- * allocate space for relation descs
- * ----------------
- */
- CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
- relationDescs = (RelationPtr)
- palloc(len * sizeof(Relation));
-
- /* ----------------
- * initialize index info array
- * ----------------
- */
- CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
- indexInfoArray = (IndexInfoPtr)
- palloc(len * sizeof(IndexInfo));
-
- for (i=0; i<len; i++)
- indexInfoArray[i] = MakeIndexInfo(0,NULL);
-
- /* ----------------
- * attempt to open each of the indices. If we succeed,
- * then store the index relation descriptor into the
- * relation descriptor array.
- * ----------------
- */
- i = 0;
- foreach (indexoid, oidList) {
- Relation indexDesc;
-
- indexOid = CInteger(CAR(indexoid));
- indexDesc = AMopen(indexOid);
- if (indexDesc != NULL)
- relationDescs[i++] = indexDesc;
- }
-
- /* ----------------
- * store the relation descriptor array and number of
- * descs into the result relation info.
- * ----------------
- */
- set_ri_NumIndices(resultRelationInfo, i);
- set_ri_IndexRelationDescs(resultRelationInfo, relationDescs);
-
- /* ----------------
- * store the index key information collected in our
- * lists into the index info array
- * ----------------
- */
- i = 0;
- foreach (numkeys, nkeyList) {
- numKeyAtts = CInteger(CAR(numkeys));
- set_ii_NumKeyAttributes(indexInfoArray[i++], numKeyAtts);
- }
-
- i = 0;
- foreach (indexkeys, keyList) {
- indexKeyAtts = (AttributeNumberPtr) CAR(indexkeys);
- set_ii_KeyAttributeNumbers(indexInfoArray[i++], indexKeyAtts);
- }
-
- i = 0;
- foreach (indexfuncs, fiList) {
- FuncIndexInfoPtr fiP = (FuncIndexInfoPtr)CAR(indexfuncs);
- set_ii_FuncIndexInfo(indexInfoArray[i++], fiP);
- }
- /* ----------------
- * store the index info array into relation info
- * ----------------
- */
- set_ri_IndexRelationInfo(resultRelationInfo, indexInfoArray);
- }
-
- /* ----------------
- * All done, resultRelationInfo now contains complete information
- * on the indices associated with the result relation.
- * ----------------
- */
-
- /* should free oidList, nkeyList and keyList here */
- }
-
- /* ----------------------------------------------------------------
- * ExecCloseIndices
- *
- * Close the index relations stored in resultRelationInfo
- * ----------------------------------------------------------------
- */
-
- /**** xxref:
- * EndPlan
- ****/
- void
- ExecCloseIndices(resultRelationInfo)
- RelationInfo resultRelationInfo;
- {
- int i;
- int numIndices;
- RelationPtr relationDescs;
-
- numIndices = get_ri_NumIndices(resultRelationInfo);
- relationDescs = get_ri_IndexRelationDescs(resultRelationInfo);
-
- for (i=0; i<numIndices; i++)
- if (relationDescs[i] != NULL)
- AMclose(relationDescs[i]);
- /*
- * XXX should free indexInfo array here too.
- */
- }
-
- /* ----------------------------------------------------------------
- * ExecFormIndexTuple
- *
- * Most of this code is cannabilized from DefaultBuild().
- * As said in the comments for ExecOpenIndices, most of
- * this functionality should be rearranged into a proper
- * set of routines..
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecInsertIndexTuples
- ****/
- IndexTuple
- ExecFormIndexTuple(heapTuple, heapRelation, indexRelation, indexInfo)
- HeapTuple heapTuple;
- Relation heapRelation;
- Relation indexRelation;
- IndexInfo indexInfo;
- {
- IndexTuple indexTuple;
- TupleDescriptor heapDescriptor;
- TupleDescriptor indexDescriptor;
- Datum *datum;
- char *nulls;
-
- int numberOfAttributes;
- AttributeNumberPtr keyAttributeNumbers;
- FuncIndexInfoPtr fInfoP;
-
- /* ----------------
- * get information from index info structure
- * ----------------
- */
- numberOfAttributes = get_ii_NumKeyAttributes(indexInfo);
- keyAttributeNumbers = get_ii_KeyAttributeNumbers(indexInfo);
- fInfoP = get_ii_FuncIndexInfo(indexInfo);
-
- /* ----------------
- * datum and null are arrays in which we collect the index attributes
- * when forming a new index tuple.
- * ----------------
- */
- CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext);
- datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
- nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
-
- /* ----------------
- * get the tuple descriptors from the relations so we know
- * how to form the index tuples..
- * ----------------
- */
- heapDescriptor = RelationGetTupleDescriptor(heapRelation);
- indexDescriptor = RelationGetTupleDescriptor(indexRelation);
-
- /* ----------------
- * FormIndexDatum fills in its datum and null parameters
- * with attribute information taken from the given heap tuple.
- * ----------------
- */
- FormIndexDatum(numberOfAttributes, /* num attributes */
- keyAttributeNumbers, /* array of att nums to extract */
- heapTuple, /* tuple from base relation */
- heapDescriptor, /* heap tuple's descriptor */
- InvalidBuffer, /* buffer associated with heap tuple */
- datum, /* return: array of attributes */
- nulls, /* return: array of char's */
- fInfoP); /* functional index information */
-
- indexTuple = FormIndexTuple(numberOfAttributes,
- indexDescriptor,
- datum,
- nulls);
-
- /* ----------------
- * free temporary arrays
- *
- * XXX should store these in the IndexInfo instead of allocating
- * and freeing on every insertion, but efficency here is not
- * that important and FormIndexTuple is wasteful anyways..
- * -cim 9/27/89
- * ----------------
- */
- pfree(nulls);
- pfree(datum);
-
- return indexTuple;
- }
-
- /* ----------------------------------------------------------------
- * ExecInsertIndexTuples
- *
- * This routine takes care of inserting index tuples
- * into all the relations indexing the result relation
- * when a heap tuple is inserted into the result relation.
- * Much of this code should be moved into the genam
- * stuff as it only exists here because the genam stuff
- * doesn't provide the functionality needed by the
- * executor.. -cim 9/27/89
- * ----------------------------------------------------------------
- */
-
- /**** xxref:
- * ExecAppend
- * ExecReplace
- ****/
- RuleLock
- ExecInsertIndexTuples(heapTuple, tupleid, estate)
- HeapTuple heapTuple;
- ItemPointer tupleid;
- EState estate;
- {
- RelationInfo resultRelationInfo;
- int i;
- int numIndices;
- RelationPtr relationDescs;
- Relation heapRelation;
- IndexInfoPtr indexInfoArray;
- IndexTuple indexTuple;
- GeneralInsertIndexResult result;
-
- /* ----------------
- * get information from the result relation info structure.
- * ----------------
- */
- resultRelationInfo = get_es_result_relation_info(estate);
- numIndices = get_ri_NumIndices(resultRelationInfo);
- relationDescs = get_ri_IndexRelationDescs(resultRelationInfo);
- indexInfoArray = get_ri_IndexRelationInfo(resultRelationInfo);
- heapRelation = get_ri_RelationDesc(resultRelationInfo);
-
- /* ----------------
- * for each index, form and insert the index tuple
- * ----------------
- */
- for (i=0; i<numIndices; i++) {
- if (relationDescs[i] != NULL) {
-
- indexTuple = ExecFormIndexTuple(heapTuple,
- heapRelation,
- relationDescs[i],
- indexInfoArray[i]);
-
- indexTuple->t_tid = (*tupleid); /* structure assignment */
-
- result = AMinsert(relationDescs[i], /* index relation */
- indexTuple, /* index tuple */
- 0, /* scan (not used) */
- 0); /* return: offset */
-
- /* XXX should inspect result for locks */
-
- /* ----------------
- * keep track of index inserts for debugging
- * ----------------
- */
- IncrIndexInserted();
-
- /* ----------------
- * free index tuple after insertion
- * ----------------
- */
- pfree(indexTuple);
- }
- }
-
- /* ----------------
- * return rule locks from insertion.. (someday)
- * ----------------
- */
- return NULL;
- }
-
- /* ----------------------------------------------------------------
- * index scan support
- * ----------------------------------------------------------------
- */
-
- /* ----------------------------------------------------------------
- * IndexNext
- *
- * Retrieve a tuple from the IndexScan node's currentRelation
- * using the indices in the IndexScanState information.
- *
- * note: the old code mentions 'Primary indices'. to my knowledge
- * we only support a single secondary index. -cim 9/11/89
- *
- * old comments:
- * retrieve a tuple from relation using the indices given.
- * The indices are used in the order they appear in 'indices'.
- * The indices may be primary or secondary indices:
- * * primary index -- scan the relation 'relID' using keys supplied.
- * * secondary index -- scan the index relation to get the 'tid' for
- * a tuple in the relation 'relID'.
- * If the current index(pointed by 'indexPtr') fails to return a
- * tuple, the next index in the indices is used.
- *
- * bug fix so that it should retrieve on a null scan key.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * UseNextIndex
- ****/
- TupleTableSlot
- IndexNext(node)
- IndexScan node;
- {
- EState estate;
- ScanState scanstate;
- IndexScanState indexstate;
- ScanDirection direction;
- int indexPtr;
- IndexScanDescPtr scanDescs;
- IndexScanDesc scandesc;
- Relation heapRelation;
- GeneralRetrieveIndexResult result;
- ItemPointer iptr;
- HeapTuple tuple;
- TupleTableSlot returnTuple;
- TupleTableSlot slot;
- Buffer buffer = InvalidBuffer;
-
- /* ----------------
- * extract necessary information from index scan node
- * ----------------
- */
- estate = (EState) get_state((Plan) node);
- direction = get_es_direction(estate);
- scanstate = get_scanstate((Scan) node);
- indexstate = get_indxstate(node);
- indexPtr = get_iss_IndexPtr(indexstate);
- scanDescs = get_iss_ScanDescs(indexstate);
- scandesc = scanDescs[ indexPtr ];
- heapRelation = get_css_currentRelation((CommonScanState) scanstate);
-
- slot = (TupleTableSlot)
- get_css_ScanTupleSlot((CommonScanState) scanstate);
-
- /* ----------------
- * ok, now that we have what we need, fetch an index tuple.
- * ----------------
- */
-
- for(;;) {
- result = AMgettuple(scandesc, direction);
- /* ----------------
- * if scanning this index succeeded then return the
- * appropriate heap tuple.. else return LispNil.
- * ----------------
- */
- if (GeneralRetrieveIndexResultIsValid(result)) {
- iptr = GeneralRetrieveIndexResultGetHeapItemPointer(result);
- tuple = heap_fetch(heapRelation,
- NowTimeQual,
- iptr,
- &buffer);
- /* be tidy */
- pfree(result);
-
- if (tuple == NULL) {
- /* ----------------
- * we found a deleted tuple, so keep on scanning..
- * ----------------
- */
- if (BufferIsValid(buffer))
- ReleaseBuffer(buffer);
- continue;
- }
-
- /* ----------------
- * store the scanned tuple in the scan tuple slot of
- * the scan state. Eventually we will only do this and not
- * return a tuple. Note: we pass 'false' because tuples
- * returned by amgetnext are pointers onto disk pages and
- * were not created with palloc() and so should not be pfree()'d.
- * ----------------
- */
- ExecStoreTuple((Pointer) tuple, /* tuple to store */
- (Pointer) slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
-
- return slot;
- }
-
- /* ----------------
- * if we get here it means the index scan failed so we
- * are at the end of the scan..
- * ----------------
- */
- return (TupleTableSlot)
- ExecClearTuple((Pointer) slot);
- }
- }
-
- /* ----------------------------------------------------------------
- * ExecIndexScan(node)
- *
- * old comments:
- * Scans the relation using primary or secondary indices and returns
- * the next qualifying tuple in the direction specified.
- * It calls ExecScan() and passes it the access methods which returns
- * the next tuple using the indices.
- *
- * Conditions:
- * -- the "cursor" maintained by the AMI is positioned at the tuple
- * returned previously.
- *
- * Initial States:
- * -- the relation indicated is opened for scanning so that the
- * "cursor" is positioned before the first qualifying tuple.
- * -- all index realtions are opened for scanning.
- * -- indexPtr points to the first index.
- * -- state variable ruleFlag = nil.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecProcNode
- ****/
- TupleTableSlot
- ExecIndexScan(node)
- IndexScan node;
- {
- TupleTableSlot returnTuple;
-
- /* ----------------
- * use IndexNext as access method
- * ----------------
- */
- returnTuple = ExecScan((Scan) node, (VoidFunctionType)IndexNext);
- return
- returnTuple;
- }
-
- /* ----------------------------------------------------------------
- * ExecIndexReScan(node)
- *
- * old comments
- * Rescans the indexed relation.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecReScan
- ****/
- List
- ExecIndexReScan(node)
- IndexScan node;
- {
- EState estate;
- IndexScanState indexstate;
- ScanDirection direction;
-
- IndexScanDescPtr scanDescs;
- ScanKeyPtr scanKeys;
- IndexScanDesc sdesc;
- ScanKey skey;
- int numIndices;
- int i;
-
- /* ----------------
- * get information from the node..
- * ----------------
- */
- estate = (EState) get_state((Plan) node);
- direction = get_es_direction(estate);
-
- indexstate = get_indxstate(node);
- numIndices = get_iss_NumIndices(indexstate);
- scanDescs = get_iss_ScanDescs(indexstate);
- scanKeys = get_iss_ScanKeys(indexstate);
-
- /* ----------------
- * rescans all indices
- *
- * note: AMrescan assumes only one scan key. This may have
- * to change if we ever decide to support multiple keys.
- * (actually I doubt the thing is even used -cim 9/11/89)
- *
- * this is actually necessary because the index scan keys are
- * copied into the scan desc in AMrescan() and when we call
- * ExecUpdateIndexScanKeys() we create a new set of keys which
- * have to be copied into the scan desc.
- * ----------------
- */
- for (i = 0; i < numIndices; i++) {
- sdesc = scanDescs[ i ];
- skey = scanKeys[ i ];
- AMrescan(sdesc, direction, skey);
- }
-
- /* ----------------
- * perhaps return something meaningful
- * ----------------
- */
- return LispNil;
- }
-
- /* ----------------------------------------------------------------
- * ExecEndIndexScan
- *
- * old comments
- * Releases any storage allocated through C routines.
- * Returns nothing.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecEndNode
- ****/
- void
- ExecEndIndexScan(node)
- IndexScan node;
- {
- ScanState scanstate;
- IndexScanState indexstate;
- ScanKeyPtr scanKeys;
- ScanKey skey;
- int numIndices;
- int i;
- AttributeNumberPtr scanAtts;
- RelationRuleInfo ruleInfo;
-
- scanstate = get_scanstate((Scan) node);
- indexstate = get_indxstate(node);
-
- /* ----------------
- * extract information from the node
- * ----------------
- */
- numIndices = get_iss_NumIndices(indexstate);
- scanKeys = get_iss_ScanKeys(indexstate);
- scanAtts = get_cs_ScanAttributes((CommonState) scanstate);
-
- /* ----------------
- * Restore the relation level rule stubs.
- * ----------------
- */
- ruleInfo = get_css_ruleInfo((CommonScanState)scanstate);
- if (ruleInfo != NULL && ruleInfo->relationStubsHaveChanged) {
- ObjectId reloid;
- reloid =
- RelationGetRelationId(
- get_css_currentRelation((CommonScanState)scanstate));
- prs2ReplaceRelationStub(reloid, ruleInfo->relationStubs);
- }
-
- /* ----------------
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(scanstate)
- * because the rule manager depends on the tupType
- * returned by ExecMain(). So for now, this
- * is freed at end-transaction time. -cim 6/2/91
- * ----------------
- */
- ExecFreeProjectionInfo((CommonState)scanstate);
- ExecFreeScanAttributes(scanAtts);
-
- /* ----------------
- * close the heap and index relations
- * ----------------
- */
- ExecCloseR((Plan) node);
-
- /* ----------------
- * free the scan keys used in scanning the indices
- * ----------------
- */
- for (i=0; i<numIndices; i++) {
- skey = scanKeys[ i ];
- ExecFreeSkeys(skey);
- }
-
- /* ----------------
- * clear out tuple table slots
- * ----------------
- */
- ExecClearTuple((Pointer)
- get_cs_ResultTupleSlot((CommonState)scanstate));
- ExecClearTuple((Pointer)
- get_css_ScanTupleSlot((CommonScanState)scanstate));
- ExecClearTuple((Pointer)
- get_css_RawTupleSlot((CommonScanState)scanstate));
- }
-
- /* ----------------------------------------------------------------
- * ExecIndexMarkPos
- *
- * old comments
- * Marks scan position by marking the current index.
- * Returns nothing.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecMarkPos
- ****/
- List
- ExecIndexMarkPos(node)
- IndexScan node;
- {
- IndexScanState indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
-
- indexstate = get_indxstate(node);
- indexPtr = get_iss_IndexPtr(indexstate);
- indexScanDescs = get_iss_ScanDescs(indexstate);
- scanDesc = indexScanDescs[ indexPtr ];
-
- /* ----------------
- * XXX access methods don't return marked positions so
- * we return LispNil.
- * ----------------
- */
- IndexScanMarkPosition( scanDesc );
- return LispNil;
-
- /* was:
- * IndexScanMarkPosition( SGetIScanDesc( SGetIndexID( SGetIndexPtr(node),
- * node)));
- */
- }
-
- /* ----------------------------------------------------------------
- * ExecIndexRestrPos
- *
- * old comments
- * Restores scan position by restoring the current index.
- * Returns nothing.
- *
- * XXX Assumes previously marked scan position belongs to current index
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecRestrPos
- ****/
- void
- ExecIndexRestrPos(node)
- IndexScan node;
- {
- IndexScanState indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
-
- indexstate = get_indxstate(node);
- indexPtr = get_iss_IndexPtr(indexstate);
- indexScanDescs = get_iss_ScanDescs(indexstate);
- scanDesc = indexScanDescs[ indexPtr ];
-
- IndexScanRestorePosition( scanDesc );
-
- /* was:
- * IndexScanRestorePosition( SGetIScanDesc( SGetIndexID( SGetIndexPtr(node),
- * node)));
- */
- }
-
- /* ----------------------------------------------------------------
- * ExecUpdateIndexScanKeys
- *
- * This recalculates the value of the scan keys whose
- * value depends on information known at runtime.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecIndexReScan
- * ExecNestLoop
- ****/
- void
- ExecUpdateIndexScanKeys(node, econtext)
- IndexScan node;
- ExprContext econtext;
- {
- IndexScanState indexstate;
- Pointer *runtimeKeyInfo;
-
- int indexPtr;
- ScanKeyPtr scanKeys;
- IntPtr numScanKeys;
-
- List indxqual;
- List qual;
- int n_keys;
- ScanKey scan_keys;
- int *run_keys;
- int j;
-
- List clause;
- Node scanexpr;
- Const scanconst;
- Datum scanvalue;
- Boolean isNull;
- Boolean isDone;
-
- /* ----------------
- * get info from the node
- * ----------------
- */
- indexstate = get_indxstate(node);
- runtimeKeyInfo = (Pointer *) get_iss_RuntimeKeyInfo(indexstate);
-
- /* ----------------
- * if runtimeKeyInfo is null, then it means all our
- * scan keys are constant so we don't have to recompute
- * anything.
- * ----------------
- */
- if (runtimeKeyInfo == NULL)
- return;
-
- /* ----------------
- * otherwise get the index qualifications and
- * recalculate the appropriate values.
- * ----------------
- */
- indexPtr = get_iss_IndexPtr(indexstate);
-
- indxqual = get_indxqual(node);
- qual = nth(indexPtr, indxqual);
-
- scanKeys = get_iss_ScanKeys(indexstate);
- numScanKeys = get_iss_NumScanKeys(indexstate);
-
- n_keys = numScanKeys[ indexPtr ];
- run_keys = (int *) runtimeKeyInfo[ indexPtr ];
- scan_keys = (ScanKey) scanKeys[ indexPtr ];
-
- for (j=0; j < n_keys; j++) {
- /* ----------------
- * if we have a run-time key, then extract the run-time
- * expression and evautate it with respect to the current
- * outer tuple. We then stick the result into the scan
- * key.
- * ----------------
- */
- if (run_keys[j] != NO_OP) {
- clause = nth(j, qual);
-
- scanexpr = (run_keys[j] == RIGHT_OP) ?
- (Node) get_rightop(clause) : (Node) get_leftop(clause) ;
-
- /* -------------
- * Pass in isDone but ignore it. We don't iterate in quals
- * -------------
- */
- scanvalue = (Datum)
- ExecEvalExpr(scanexpr, econtext, &isNull, &isDone);
-
- ExecSetSkeysValue(j, /* index into scan_keys array */
- scan_keys, /* array of scan keys */
- scanvalue); /* const computed from outerTuple */
- }
- }
- }
-
- /* ----------------------------------------------------------------
- * ExecInitIndexScan
- *
- * Initializes the index scan's state information, creates
- * scan keys, and opens the base and index relations.
- *
- * Note: index scans have 2 sets of state information because
- * we have to keep track of the base relation and the
- * index relations.
- *
- * old comments
- * Creates the run-time state information for the node and
- * sets the relation id to contain relevant decriptors.
- *
- * Parameters:
- * node: IndexNode node produced by the planner.
- * estate: the execution state initialized in InitPlan.
- * ----------------------------------------------------------------
- */
- /**** xxref:
- * ExecInitNode
- ****/
- List
- ExecInitIndexScan(node, estate, parent)
- IndexScan node;
- EState estate;
- Plan parent;
- {
- IndexScanState indexstate;
- ScanState scanstate;
- List indxqual;
- List indxid;
- int i;
- int numIndices;
- int indexPtr;
- ScanKeyPtr scanKeys;
- IntPtr numScanKeys;
- RelationPtr relationDescs;
- IndexScanDescPtr scanDescs;
- Pointer *runtimeKeyInfo;
- bool have_runtime_keys;
- List rangeTable;
- List rtentry;
- Index relid;
- ObjectId reloid;
- TimeQual timeQual;
-
- Relation currentRelation;
- HeapScanDesc currentScanDesc;
- ScanDirection direction;
- ParamListInfo paraminfo;
- ExprContext econtext;
- int baseid;
-
- /* ----------------
- * assign execution state to node
- * ----------------
- */
- set_state((Plan) node, (EStatePtr)estate);
-
- /* --------------------------------
- * Part 1) initialize scan state
- *
- * create new ScanState for node
- * --------------------------------
- */
- scanstate = MakeScanState(false, 0);
- set_scanstate((Scan) node, scanstate);
-
- /* ----------------
- * assign node's base_id .. we don't use AssignNodeBaseid() because
- * the increment is done later on after we assign the index scan's
- * scanstate. see below.
- * ----------------
- */
- baseid = get_es_BaseId(estate);
- set_base_id((BaseNode) scanstate, baseid);
-
- /* ----------------
- * create expression context for node
- * ----------------
- */
- ExecAssignExprContext(estate, (CommonState) scanstate);
-
- #define INDEXSCAN_NSLOTS 3
- /* ----------------
- * tuple table initialization
- * ----------------
- */
- ExecInitResultTupleSlot(estate, (CommonState) scanstate);
- ExecInitScanTupleSlot(estate, (CommonScanState)scanstate);
- ExecInitRawTupleSlot(estate, (CommonScanState)scanstate);
-
- /* ----------------
- * initialize projection info. result type comes from scan desc
- * below..
- * ----------------
- */
- ExecAssignProjectionInfo((Plan) node, (CommonState)scanstate);
-
- /* ----------------
- * initialize scanAttributes (used by rule manager)
- * ----------------
- */
- ExecInitScanAttributes((Plan) node);
-
- /* --------------------------------
- * Part 2) initialize index scan state
- *
- * create new IndexScanState for node
- * --------------------------------
- */
- indexstate = MakeIndexScanState(0, 0, NULL, NULL, NULL, NULL, NULL);
- set_indxstate(node, indexstate);
-
- /* ----------------
- * assign base id to index scan state also
- * ----------------
- */
- set_base_id((BaseNode) indexstate, baseid);
- baseid++;
- set_es_BaseId(estate, baseid);
-
- /* ----------------
- * assign debugging hooks
- * ----------------
- */
- ExecAssignDebugHooks((Plan) node, (BaseNode) indexstate);
-
- /* ----------------
- * get the index node information
- * ----------------
- */
- indxid = get_indxid(node);
- indxqual = get_indxqual(node);
- numIndices = length(indxid);
- indexPtr = 0;
-
- CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
-
- numScanKeys = (IntPtr) palloc(numIndices * sizeof(int));
- scanKeys = (ScanKeyPtr) palloc(numIndices * sizeof(ScanKey));
- relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
- scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
-
- /* ----------------
- * initialize runtime key info.
- * ----------------
- */
- have_runtime_keys = false;
- runtimeKeyInfo = (Pointer *)
- palloc(numIndices * sizeof(Pointer));
-
- /* ----------------
- * build the index scan keys from the index qualification
- * ----------------
- */
- for (i=0; i < numIndices; i++) {
- int j;
- List qual;
- int n_keys;
- ScanKey scan_keys;
- int *run_keys;
-
- qual = nth(i, indxqual);
- n_keys = length(qual);
- scan_keys = (ScanKey) ExecMakeSkeys(n_keys);
-
- CXT1_printf("ExecInitIndexScan: context is %d\n",
- CurrentMemoryContext);
-
- if (n_keys > 0) {
- run_keys = (int *) palloc(n_keys * sizeof(int));
- }
-
- /* ----------------
- * for each opclause in the given qual,
- * convert each qual's opclause into a single scan key
- * ----------------
- */
- for (j=0; j < n_keys; j++) {
- List clause; /* one part of index qual */
- Oper op; /* operator used in scan.. */
- Node leftop; /* expr on lhs of operator */
- Node rightop; /* expr on rhs ... */
-
- int scanvar; /* which var identifies varattno */
- int varattno; /* att number used in scan */
- ObjectId opid; /* operator id used in scan */
- Datum scanvalue; /* value used in scan (if const) */
-
- /* ----------------
- * extract clause information from the qualification
- * ----------------
- */
- clause = nth(j, qual);
-
- op = (Oper) get_op(clause);
- if (! ExactNodeType(op,Oper))
- elog(WARN, "ExecInitIndexScan: op not an Oper!");
-
- opid = get_opid(op);
-
- /* ----------------
- * Here we figure out the contents of the index qual.
- * The usual case is (op var const) or (op const var)
- * which means we form a scan key for the attribute
- * listed in the var node and use the value of the const.
- *
- * If we don't have a const node, then it means that
- * one of the var nodes refers to the "scan" tuple and
- * is used to determine which attribute to scan, and the
- * other expression is used to calculate the value used in
- * scanning the index.
- *
- * This means our index scan's scan key is a function of
- * information obtained during the execution of the plan
- * in which case we need to recalculate the index scan key
- * at run time.
- *
- * Hence, we set have_runtime_keys to true and then set
- * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP.
- * The corresponding scan keys are recomputed at run time.
- * ----------------
- */
-
- scanvar = NO_OP;
-
- /* ----------------
- * determine information in leftop
- * ----------------
- */
- leftop = (Node) get_leftop(clause);
-
- if (ExactNodeType(leftop,Var) && var_is_rel(leftop)) {
- /* ----------------
- * if the leftop is a "rel-var", then it means
- * that it is a var node which tells us which
- * attribute to use for our scan key.
- * ----------------
- */
- varattno = get_varattno((Var) leftop);
- scanvar = LEFT_OP;
- } else if (ExactNodeType(leftop,Const)) {
- /* ----------------
- * if the leftop is a const node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
- run_keys[ j ] = NO_OP;
- scanvalue = get_constvalue((Const) leftop);
- } else if (consp(leftop) &&
- ExactNodeType(CAR((List)leftop),Func) &&
- var_is_rel(CADR((List)leftop))) {
- /* ----------------
- * if the leftop is a func node then it means
- * it identifies the value to place in our scan key.
- * Since functional indices have only one attribute
- * the attno must always be set to 1.
- * ----------------
- */
- varattno = 1;
- scanvar = LEFT_OP;
-
- } else {
- /* ----------------
- * otherwise, the leftop contains information usable
- * at runtime to figure out the value to place in our
- * scan key.
- * ----------------
- */
- have_runtime_keys = true;
- run_keys[ j ] = LEFT_OP;
- scanvalue = get_constvalue(ConstTrue);
- }
-
- /* ----------------
- * now determine information in rightop
- * ----------------
- */
- rightop = (Node) get_rightop(clause);
-
- if (ExactNodeType(rightop,Var) && var_is_rel(rightop)) {
- /* ----------------
- * here we make sure only one op identifies the
- * scan-attribute...
- * ----------------
- */
- if (scanvar == LEFT_OP)
- elog(WARN, "ExecInitIndexScan: %s",
- "both left and right op's are rel-vars");
-
- /* ----------------
- * if the rightop is a "rel-var", then it means
- * that it is a var node which tells us which
- * attribute to use for our scan key.
- * ----------------
- */
- varattno = get_varattno((Var) rightop);
- scanvar = RIGHT_OP;
-
- } else if (ExactNodeType(rightop,Const)) {
- /* ----------------
- * if the leftop is a const node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
- run_keys[ j ] = NO_OP;
- scanvalue = get_constvalue((Const) rightop);
-
- } else if (consp(rightop) &&
- ExactNodeType(CAR((List)rightop),Func) &&
- var_is_rel(CADR((List)rightop))) {
- /* ----------------
- * if the rightop is a func node then it means
- * it identifies the value to place in our scan key.
- * Since functional indices have only one attribute
- * the attno must always be set to 1.
- * ----------------
- */
- if (scanvar == LEFT_OP)
- elog(WARN, "ExecInitIndexScan: %s",
- "both left and right ops are rel-vars");
-
- varattno = 1;
- scanvar = RIGHT_OP;
-
- } else {
- /* ----------------
- * otherwise, the leftop contains information usable
- * at runtime to figure out the value to place in our
- * scan key.
- * ----------------
- */
- have_runtime_keys = true;
- run_keys[ j ] = RIGHT_OP;
- scanvalue = get_constvalue(ConstTrue);
- }
-
- /* ----------------
- * now check that at least one op tells us the scan
- * attribute...
- * ----------------
- */
- if (scanvar == NO_OP)
- elog(WARN, "ExecInitIndexScan: %s",
- "neither leftop nor rightop refer to scan relation");
-
- /* ----------------
- * initialize the scan key's fields appropriately
- *
- * XXX UGLY, UGLY, UGLY cast here. scan_keys is an array of
- * pointers to ScanKeyData structs and the macro first
- * dereferences then takes address of it and passes this
- * off as a ScanKeyEntry. It works but its not right.
- * -- mer 16 Nov. 1991
- * ----------------
- */
- ExecSetSkeys(j, /* index into scan_keys array */
- /* array in which to plug scan key */
- (ScanKeyEntry)scan_keys,
- /* attribute number to scan */
- (AttributeNumber) varattno,
- (RegProcedure) opid, /* reg proc to use */
- (Datum) scanvalue); /* constant */
- }
-
- /* ----------------
- * store the key information into our array.
- * ----------------
- */
- numScanKeys[ i ] = n_keys;
- scanKeys[ i ] = scan_keys;
- runtimeKeyInfo[ i ] = (Pointer) run_keys;
- }
-
- set_iss_NumIndices(indexstate, numIndices);
- set_iss_IndexPtr(indexstate, indexPtr);
- set_iss_ScanKeys(indexstate, scanKeys);
- set_iss_NumScanKeys(indexstate, numScanKeys);
-
- /* ----------------
- * If all of our keys have the form (op var const) , then we have no
- * runtime keys so we store NULL in the runtime key info.
- * Otherwise runtime key info contains an array of pointers
- * (one for each index) to arrays of flags (one for each key)
- * which indicate that the qual needs to be evaluated at runtime.
- * -cim 10/24/89
- * ----------------
- */
- if (have_runtime_keys)
- {
- set_iss_RuntimeKeyInfo(indexstate, (Pointer) runtimeKeyInfo);
- }
- else {
- set_iss_RuntimeKeyInfo(indexstate, NULL);
- for (i=0; i < numIndices; i++) {
- List qual;
- int n_keys;
- qual = nth(i, indxqual);
- n_keys = length(qual);
- if (n_keys > 0)
- pfree(runtimeKeyInfo[i]);
- }
- pfree(runtimeKeyInfo);
- }
-
- /* ----------------
- * get the range table and direction information
- * from the execution state (these are needed to
- * open the relations).
- * ----------------
- */
- rangeTable = get_es_range_table(estate);
- direction = get_es_direction(estate);
-
- /* ----------------
- * open the base relation
- * ----------------
- */
- relid = get_scanrelid((Scan) node);
- rtentry = rt_fetch(relid, rangeTable);
- reloid = CInteger(rt_relid(rtentry));
- timeQual = (TimeQual) CInteger(rt_time(rtentry));
-
- ExecOpenScanR(reloid, /* relation */
- 0, /* nkeys */
- (ScanKey) NULL, /* scan key */
- 0, /* is index */
- direction, /* scan direction */
- timeQual, /* time qual */
- ¤tRelation, /* return: rel desc */
- (Pointer *) ¤tScanDesc); /* return: scan desc */
-
- set_css_currentRelation((CommonScanState) scanstate, currentRelation);
- set_css_currentScanDesc((CommonScanState)scanstate, currentScanDesc);
- /*
- * intialize scan state rule info
- */
- set_css_ruleInfo((CommonScanState)scanstate,
- prs2MakeRelationRuleInfo(currentRelation, RETRIEVE));
-
-
- /* ----------------
- * XXX temporary hack to prevent tuple level locks
- * from exhausting our semaphores. Remove this
- * when the tuple level lock stuff is fixed.
- * ----------------
- */
- RelationSetLockForRead(currentRelation);
-
- /* ----------------
- * get the scan type from the relation descriptor.
- * ----------------
- */
- ExecAssignScanType((CommonScanState)scanstate,
- TupDescToExecTupDesc(¤tRelation->rd_att,
- currentRelation->rd_rel->relnatts),
- ¤tRelation->rd_att); /* bug -- glass */
- ExecAssignResultTypeFromTL((Plan) node, (CommonState)scanstate);
-
- /* ----------------
- * index scans don't have subtrees..
- * ----------------
- */
- set_ss_ProcOuterFlag(scanstate, false);
-
- /* ----------------
- * open the index relations and initialize
- * relation and scan descriptors.
- * ----------------
- */
- for (i=0; i < numIndices; i++) {
- List index;
- ObjectId indexOid;
-
- index = nth(i, indxid);
- indexOid = CInteger(index);
-
- if (indexOid != 0) {
- ExecOpenScanR(indexOid, /* relation */
- numScanKeys[ i ], /* nkeys */
- scanKeys[ i ], /* scan key */
- true, /* is index */
- direction, /* scan direction */
- timeQual, /* time qual */
- &(relationDescs[ i ]), /* return: rel desc */
- (Pointer *) &(scanDescs[ i ]));
- /* return: scan desc */
- }
- }
-
- set_iss_RelationDescs(indexstate, relationDescs);
- if (ParallelExecutorEnabled() && SlaveLocalInfoD.nparallel > 1)
- partition_indexscan(numIndices, scanDescs, SlaveLocalInfoD.nparallel);
- set_iss_ScanDescs(indexstate, scanDescs);
-
- set_cs_TupFromTlist((CommonState)indexstate, false);
-
- /* ----------------
- * all done.
- * ----------------
- */
- return
- LispTrue;
- }
-
- int
- ExecCountSlotsIndexScan(node)
- Plan node;
- {
- return ExecCountSlotsNode(get_outerPlan(node)) +
- ExecCountSlotsNode(get_innerPlan(node)) +
- INDEXSCAN_NSLOTS;
- }
-
- /* ---------------------------------------------------------
- * partition_indexscan
- *
- * XXXXXX this function now only works for indexes on
- * int2 and int4 and does handle skewed data distribution.
- * more elaborate schemes will be implemented later.
- *
- * --------------------------------------------------------
- */
-
- #define INT4LT 66
- #define INT4LE 149
- #define INT4GT 147
- #define INT4GE 150
- #define INT2LT 64
- #define INT2LE 148
- #define INT2GT 146
- #define INT2GE 151
-
- void
- partition_indexscan(numIndices, scanDescs, parallel)
- int numIndices;
- IndexScanDescPtr scanDescs;
- int parallel;
- {
- int i;
- IndexScanDesc scanDesc;
- ScanKeyEntryData lowKeyEntry;
- ScanKeyEntryData highKeyEntry;
- int32 lowKey, newlowkey;
- int32 highKey, newhighkey;
- int delt;
-
- for (i=0; i<numIndices; i++) {
- scanDesc = scanDescs[i];
- if (scanDesc->numberOfKeys < 2) {
- /* -------------------------------------
- * don't know how to partition, just return
- * -------------------------------------
- */
- return;
- }
- lowKeyEntry = scanDesc->keyData.data[0];
-
- switch (lowKeyEntry.procedure) {
- case INT4GT:
- case INT4GE:
- highKeyEntry = scanDesc->keyData.data[1];
- break;
-
- case INT4LT:
- case INT4LE:
- highKeyEntry = lowKeyEntry;
- lowKeyEntry = scanDesc->keyData.data[1];
- break;
-
- defaults:
- /* -------------------------------------
- * don't know how to partition, just return
- * -------------------------------------
- */
- return;
- }
-
- lowKey = DatumGetInt32(lowKeyEntry.argument);
- highKey = DatumGetInt32(highKeyEntry.argument);
- delt = highKey - lowKey;
-
- Assert(delt > 0);
-
- newlowkey = lowKey + SlaveInfoP[MyPid].groupPid * delt / parallel;
- newhighkey = lowKey + (SlaveInfoP[MyPid].groupPid + 1) * delt/parallel;
-
- lowKeyEntry.argument = Int32GetDatum(newlowkey);
-
- if (SlaveInfoP[MyPid].groupPid > 0) {
- lowKeyEntry.procedure = INT4GE;
- fmgr_info(INT4GE, &lowKeyEntry.func, &lowKeyEntry.nargs);
- }
-
- highKeyEntry.argument = Int32GetDatum(newhighkey);
-
- if (SlaveInfoP[MyPid].groupPid < parallel - 1) {
- highKeyEntry.procedure = INT4LT;
- fmgr_info(INT4LT, &highKeyEntry.func, &highKeyEntry.nargs);
- }
-
- scanDesc->keyData.data[0] = lowKeyEntry;
- scanDesc->keyData.data[1] = highKeyEntry;
- }
- }
-